﻿using ScottPlot;
using SkiaSharp;
using System;
using UtilZ.Dotnet.Ex.Model;

namespace UtilZ.Dotnet.ScottPlotEx
{
    internal abstract class WaterfallPlotAbs : IDisposable
    {
        protected readonly int _framCount;
        protected readonly int _fftLength;
        protected readonly ScottPlot.Range _dataRange;
        protected readonly IColormap _colormap;
        protected readonly PlotMoveDirection _moveOrientation;
        protected readonly WaterfallPlotOptions _options;

        protected readonly SKPaint _paint;
        protected readonly SKBitmap _bitmap;
        protected readonly SKImageInfo _imageInfo;
        protected readonly CoordinateRect _alignedExtent;
        protected readonly double _cellWidth;
        protected readonly double _cellHeight;

        protected readonly bool _cacheOriginValue = false;
        protected readonly UnmanagedMemoryPointer<double> _originValuesPointer = new UnmanagedMemoryPointer<double>();
        protected readonly UnmanagedMemoryPointer<uint> _argbsPointer = new UnmanagedMemoryPointer<uint>();
        protected int _argsbWriteOffset = 0;
        protected readonly int _imgPixs = 0;
        protected bool _argbsIsUpdated = false;

        /// <summary>
        /// 垂直翻转
        /// </summary>
        protected readonly bool _flipVertically = false;

        /// <summary>
        /// 水平翻转
        /// </summary>
        protected readonly bool _flipHorizontally = false;

        /// <summary>
        /// Nan值点的颜色
        /// </summary>
        protected readonly ScottPlot.Color _naNCellColor = Colors.Transparent;


        public WaterfallPlotAbs(int fftLength, int framCount, ScottPlot.Range dataRange, IColormap colormap, PlotMoveDirection moveOrientation, WaterfallPlotOptions options)
        {
            this._dataRange = dataRange;
            this._colormap = colormap;
            this._moveOrientation = moveOrientation;
            this._framCount = framCount;
            this._fftLength = fftLength;
            this._imgPixs = framCount * fftLength;
            this._argbsPointer.CheckSetLength(this._imgPixs * 2);
            this._argbsPointer.GetSpan().Fill(colormap.GetColor(dataRange.Min, this._dataRange).PremultipliedARGB);
            this._argsbWriteOffset = this._imgPixs;

            this._cacheOriginValue = options.CacheOriginValue;
            if (this._cacheOriginValue)
            {
                this._originValuesPointer.CheckSetLength(this._argbsPointer.Length);
                this._originValuesPointer.GetSpan().Fill(dataRange.Min);
            }
            else
            {
                this._originValuesPointer.Length = 0;
            }


            this._imageInfo = new SKImageInfo(fftLength, framCount);
            this._bitmap = new SKBitmap(this._imageInfo);
            this._paint = new SKPaint()
            {
                FilterQuality = options.Smooth ? SKFilterQuality.High : SKFilterQuality.None
            };

            this._flipVertically = options.FlipVertically;
            this._flipHorizontally = options.FlipHorizontally;
            this._naNCellColor = options.NaNCellColor;
            this._options = options;
        }

        ~WaterfallPlotAbs()
        {
            this.PrimitiveDispose();
        }






        protected void PrimitiveAppendData_Flip(Span<double> specData, Span<uint> argbs, Span<double> originValues)
        {
            uint nanCellArgb = this._naNCellColor.PremultipliedARGB;
            ScottPlot.Range range = this._dataRange;
            int index = this._fftLength - 1;
            double value;

            if (this._cacheOriginValue)
            {
                for (int i = 0; i < specData.Length; i++)
                {
                    value = specData[i];
                    originValues[index] = value;
                    if (double.IsNaN(value))
                    {
                        argbs[index] = nanCellArgb;
                    }
                    else
                    {
                        argbs[index] = this._colormap.GetColor(value, range).PremultipliedARGB;
                    }
                    index--;
                }
            }
            else
            {
                for (int i = 0; i < specData.Length; i++)
                {
                    value = specData[i];
                    if (double.IsNaN(value))
                    {
                        argbs[index] = nanCellArgb;
                    }
                    else
                    {
                        argbs[index] = this._colormap.GetColor(value, range).PremultipliedARGB;
                    }
                    index--;
                }
            }
        }

        protected void PrimitiveAppendData(Span<double> specData, Span<uint> argbs, Span<double> originValues)
        {
            uint nanCellArgb = this._naNCellColor.PremultipliedARGB;
            ScottPlot.Range range = this._dataRange;
            double value;

            if (this._cacheOriginValue)
            {
                for (int i = 0; i < specData.Length; i++)
                {
                    value = specData[i];
                    originValues[i] = value;
                    if (double.IsNaN(value))
                    {
                        argbs[i] = nanCellArgb;
                    }
                    else
                    {
                        argbs[i] = this._colormap.GetColor(value, range).PremultipliedARGB;
                    }
                }
            }
            else
            {
                for (int i = 0; i < specData.Length; i++)
                {
                    value = specData[i];
                    if (double.IsNaN(value))
                    {
                        argbs[i] = nanCellArgb;
                    }
                    else
                    {
                        argbs[i] = this._colormap.GetColor(value, range).PremultipliedARGB;
                    }
                }
            }
        }














        private bool _disposed = false;
        /// <summary>
        /// 释放资源
        /// </summary>
        public void Dispose()
        {
            this.PrimitiveDispose();
        }


        private void PrimitiveDispose()
        {
            if (this._disposed)
            {
                return;
            }
            this._disposed = true;

            this._paint.Dispose();
            this._bitmap.Dispose();
            this._originValuesPointer.Dispose();
            this._argbsPointer.Dispose();
        }



    }
}
